package com.jmm.api.service.impl.user;

import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jmm.api.dao.sms.ApiSmsValidateMapper;
import com.jmm.api.dao.user.ApiAppUserMapper;
import com.jmm.api.dao.user.ApiOauthMapper;
import com.jmm.api.pojo.domain.sms.ApiSmsValidateDO;
import com.jmm.api.pojo.domain.user.ApiAppUserDO;
import com.jmm.api.pojo.domain.user.ApiOauthDO;
import com.jmm.api.pojo.vo.login.TokenVO;
import com.jmm.api.service.user.UserService;
import com.jmm.common.constant.EnumLoginType;
import com.jmm.common.constant.RegConstants;
import com.jmm.common.exception.YogoException;
import com.jmm.common.utils.IdUtil;
import com.jmm.common.utils.JWTUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import com.jmm.api.config.JWTConfig;
import com.jmm.common.config.YogoConfig;
import com.jmm.common.type.EnumErrorCode;
import com.jmm.common.utils.SpringContextHolder;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * <pre>
 * </pre>
 *
 * <small> 2018年4月27日 |  </small>
 */
@Service
@Transactional
public class UserServiceImpl extends ServiceImpl<ApiAppUserMapper,ApiAppUserDO> implements UserService {
    /** Holder for lazy-init */
    private static class Holder {
        static final JWTConfig jwt = SpringContextHolder.getBean(YogoConfig.class).getJwt();
    }
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    Log log = LogFactory.get();

    @Autowired
    private ApiOauthMapper apiOauthMapper;
    @Autowired
    private ApiSmsValidateMapper apiSmsValidateMapper;

    @Override
    public TokenVO getToken(String uname, String passwd, String socialToken) {
        //匹配账号密码
        log.info("用户登录：{}",uname);
        if(!ReUtil.isMatch(RegConstants.PHONE_REG, String.valueOf(uname))){
            log.info("手机号格式错误：{}",uname);
            throw new YogoException(EnumErrorCode.apiSmsPhoneNotMatch.getCodeStr());
        }
        checkPwd(passwd);

        String password = DigestUtils.sha1Hex(passwd);
        ApiOauthDO apiOauthDO = new ApiOauthDO();
        apiOauthDO.setOauthId(uname);
        apiOauthDO.setOauthAccessToken(password);
        apiOauthDO.setOauthType(EnumLoginType.PHONE.getLoginTypeCode());
        ApiOauthDO user = apiOauthMapper.selectOne(new QueryWrapper<>(apiOauthDO));
        if (null == user) {
            log.info("不存在该用户：{}",uname);
            throw new YogoException(EnumErrorCode.apiAuthorizationLoginFailed.getCodeStr());
        }
        /**
         * 绑定userId
         */
        if(!StrUtil.isEmpty(socialToken)){
            socialBinding(user,socialToken);
        }
        return createToken(user);
    }
    @Override
    public TokenVO getSmsToken(String uname, String smscode,String socialToken) {
        //匹配账号密码
        log.info("用户登录：{}",uname);
        if(!ReUtil.isMatch(RegConstants.PHONE_REG, String.valueOf(uname))){
            log.info("手机号格式错误：{}",uname);
            throw new YogoException(EnumErrorCode.apiSmsPhoneNotMatch.getCodeStr());
        }
        ApiSmsValidateDO smsDo = new ApiSmsValidateDO();
        smsDo.setPhone(Long.parseLong(uname));
        smsDo.setCode(smscode);
        smsDo.setUsed(0);
        ApiSmsValidateDO apiSmsValidateDO = apiSmsValidateMapper.selectOne(new QueryWrapper<>(smsDo));
        //判断验证码是否正确
        if(apiSmsValidateDO == null){
            log.info("验证码不正确");
            throw new YogoException(EnumErrorCode.apiAuthorizationRegisterFailed.getCodeStr());
        }
        if(apiSmsValidateDO.getExpirationTime()<System.currentTimeMillis()){
            log.info("验证码超时");
            throw new YogoException(EnumErrorCode.apiAuthorizationRegisterFailed.getCodeStr());
        }

        ApiOauthDO apiOauthDO = new ApiOauthDO();
        apiOauthDO.setOauthId(uname);
        apiOauthDO.setOauthType(EnumLoginType.PHONE.getLoginTypeCode());
        ApiOauthDO user = apiOauthMapper.selectOne(new QueryWrapper<>(apiOauthDO));
        if (null == user) {
            log.info("不存在该用户：{}",uname);
            throw new YogoException(EnumErrorCode.apiPhoneNotExist.getCodeStr());
        }

        apiSmsValidateDO.setUsed(1);
        apiSmsValidateMapper.updateById(apiSmsValidateDO);
        /**
         * 绑定userId
         */
        if(!StrUtil.isEmpty(socialToken)){
            socialBinding(user,socialToken);
        }
        return createToken(user);
    }

    /**
     * 判断是否绑定用户
     * @param user
     * @param socialToken
     */
    private void socialBinding(ApiOauthDO user,String socialToken){
        try{
            log.info("存在用户：{},判断是否绑定",user.getOauthId());
            ApiOauthDO toWechatUser = new ApiOauthDO();
            toWechatUser.setUserId(user.getUserId());
            log.info("判断是否存在微信绑定");
            toWechatUser.setOauthType(EnumLoginType.WECHAT.getLoginTypeCode());
            ApiOauthDO apiOauthDO = apiOauthMapper.selectOne(new QueryWrapper<>(toWechatUser));
            if(apiOauthDO == null){
                log.info("不存在微信绑定,判断SocialToken是否合法");
                String oauthId = JWTUtil.getUserId(socialToken);
                log.info("执行用户绑定的openId:{}",oauthId);
                if(!StrUtil.isEmpty(oauthId) && JWTUtil.verify(socialToken, oauthId, oauthId,Holder.jwt.getSoTokenBizKey())){
                    toWechatUser.setOauthId(oauthId);
                    toWechatUser.setId(IdUtil.getStrId());
                    apiOauthMapper.insert(toWechatUser);
                }else {
                    log.info("SocialToken不合法");
                }

            }else{
                log.info("已存在微信绑定");
            }
        }catch (Exception e){
            log.error(e,e.toString());
        }
    }

    @Override
    public void register(String uname,String passwd,String smscode){
        log.info("用户注册：{}",uname);
        if(!ReUtil.isMatch(RegConstants.PHONE_REG, String.valueOf(uname))){
            log.info("手机号格式错误：{}",uname);
            throw new YogoException(EnumErrorCode.apiSmsPhoneNotMatch.getCodeStr());
        }
        checkPwd(passwd);
        ApiSmsValidateDO smsDo = new ApiSmsValidateDO();
        smsDo.setPhone(Long.parseLong(uname));
        smsDo.setCode(smscode);
        smsDo.setUsed(0);
        ApiSmsValidateDO apiSmsValidateDO = apiSmsValidateMapper.selectOne(new QueryWrapper<>(smsDo));
        //判断验证码是否正确
        if(apiSmsValidateDO == null){
            log.info("验证码不正确");
            throw new YogoException(EnumErrorCode.apiAuthorizationRegisterFailed.getCodeStr());
        }
        if(apiSmsValidateDO.getExpirationTime()<System.currentTimeMillis()){
            log.info("验证码超时");
            throw new YogoException(EnumErrorCode.apiAuthorizationRegisterFailed.getCodeStr());
        }
        apiSmsValidateDO.setUsed(1);
        apiSmsValidateMapper.updateById(apiSmsValidateDO);
        ApiAppUserDO appUserDo = new ApiAppUserDO();
        appUserDo.setId(IdUtil.getStrId());
        appUserDo.setCreateTime(new Date());
        appUserDo.setNickName(uname);
        save(appUserDo);
        ApiOauthDO oauthDo = new ApiOauthDO();
        oauthDo.setId(IdUtil.getStrId());
        oauthDo.setOauthId(uname);
        oauthDo.setOauthType(EnumLoginType.PHONE.getLoginTypeCode());
        oauthDo.setUserId(appUserDo.getId());
        String password = DigestUtils.sha1Hex(passwd);
        oauthDo.setOauthAccessToken(password);
        apiOauthMapper.insert(oauthDo);
    }
    @Override
    public void forget(String uname,String passwd,String smscode){
        log.info("用户忘记密码：{}",uname);
        if(!ReUtil.isMatch(RegConstants.PHONE_REG, String.valueOf(uname))){
            log.info("手机号格式错误：{}",uname);
            throw new YogoException(EnumErrorCode.apiSmsPhoneNotMatch.getCodeStr());
        }
        checkPwd(passwd);
        ApiSmsValidateDO smsDo = new ApiSmsValidateDO();
        smsDo.setPhone(Long.parseLong(uname));
        smsDo.setCode(smscode);
        smsDo.setUsed(0);
        ApiSmsValidateDO apiSmsValidateDO = apiSmsValidateMapper.selectOne(new QueryWrapper<>(smsDo));
        //判断验证码是否正确
        if(apiSmsValidateDO == null){
            log.info("验证码不正确");
            throw new YogoException(EnumErrorCode.apiAuthorizationRegisterFailed.getCodeStr());
        }
        if(apiSmsValidateDO.getExpirationTime()<System.currentTimeMillis()){
            log.info("验证码超时");
            throw new YogoException(EnumErrorCode.apiAuthorizationRegisterFailed.getCodeStr());
        }
        apiSmsValidateDO.setUsed(1);
        apiSmsValidateMapper.updateById(apiSmsValidateDO);
        ApiOauthDO oauthDo = new ApiOauthDO();
        String password = DigestUtils.sha1Hex(passwd);
        oauthDo.setOauthAccessToken(password);
        Wrapper<ApiOauthDO> wrapper = new QueryWrapper<ApiOauthDO>();
        apiOauthMapper.update(oauthDo,new UpdateWrapper<ApiOauthDO>().lambda()
                .eq(ApiOauthDO::getOauthId,uname)
                .eq(ApiOauthDO::getOauthType,EnumLoginType.PHONE.getLoginTypeCode()));
    }

    /**
     * 校验密码安全度
     * @param passwd
     */
    private void checkPwd(String passwd) {
        if(ReUtil.isMatch(RegConstants.PASSWORD_LV1_REG, String.valueOf(passwd))){
            throw new YogoException(EnumErrorCode.apiPassWdNotMatchLV1.getCodeStr());
        }
        if(ReUtil.isMatch(RegConstants.PASSWORD_LV2_1_REG, String.valueOf(passwd))){
            throw new YogoException(EnumErrorCode.apiPassWdNotMatchLV2.getCodeStr());
        }
        if(ReUtil.isMatch(RegConstants.PASSWORD_LV2_2_REG, String.valueOf(passwd))){
            throw new YogoException(EnumErrorCode.apiPassWdNotMatchLV2.getCodeStr());
        }
        if(!ReUtil.isMatch(RegConstants.PASSWORD_OK_REG, String.valueOf(passwd))){
            throw new YogoException(EnumErrorCode.apiPassWdNotMatch.getCodeStr());
        }
    }

    @Override
    public boolean verifyToken(String token) {
        //解析ID token为userId refreshToken为oauthId
        log.info("校验token是否合法");
        String userId = null;
        //ApiOauthDO user = null;
        if (StringUtils.isNotBlank(token)
                && (userId = JWTUtil.getUserId(token)) != null && JWTUtil.verify(token, userId, userId,Holder.jwt.getTokenBizKey())
                && notLogout(token,false,userId)) {
            return true;
        }else {
            return false;
        }
    }

    @Override
    public TokenVO refreshToken(String uname, String refreshToken) {
        log.info("刷新Token：{}",uname);
        if(!ReUtil.isMatch(RegConstants.PHONE_REG, String.valueOf(uname))){
            log.info("手机号格式错误：{}",uname);
            throw new YogoException(EnumErrorCode.apiSmsPhoneNotMatch.getCodeStr());
        }
        ApiOauthDO user = null;
        if(StringUtils.isNotBlank(refreshToken)
                && uname.equals(JWTUtil.getUserId(refreshToken))){
            ApiOauthDO apiOauthDO = new ApiOauthDO();
            apiOauthDO.setOauthType(1);
            apiOauthDO.setOauthId(uname);
            user = apiOauthMapper.selectOne(new QueryWrapper<>(apiOauthDO));
            if(user != null && JWTUtil.verify(refreshToken, user.getOauthId(), user.getOauthId(),Holder.jwt.getReTokenBizKey())){
                if(!notLogout(refreshToken,true,user.getUserId())){
                    throw new YogoException(EnumErrorCode.apiAuthorizationInvalid.getCodeStr());
                }
                return createToken(user);
            }
        }
        throw new YogoException(EnumErrorCode.apiAuthorizationInvalid.getCodeStr());
    }

    @Override
    public Boolean logoutToken(String token, String refreshToken) {
        String userId = JWTUtil.getUserId(token);
        if(StrUtil.isEmpty(userId)){
            return false;
        }
        stringRedisTemplate.delete("api_token|"+userId+"|t|"+token);
        stringRedisTemplate.delete("api_token|"+userId+"|rt|"+refreshToken);
        return true;
    }

    @Override
    public TokenVO createToken(ApiOauthDO user) {
        log.info("创建Token:{}",user.getOauthId());
        TokenVO vo = new TokenVO();
        String token = JWTUtil.sign(String.valueOf(user.getUserId()), String.valueOf(user.getUserId()), Holder.jwt.getTokenBizKey(), Holder.jwt.getExpireTime());
        String refreshToken = JWTUtil.sign(user.getOauthId(), user.getOauthId(), Holder.jwt.getReTokenBizKey(), Holder.jwt.getRefreshTokenExpire());
        vo.setToken(token);
        vo.setRefreshToken(refreshToken);
        vo.setTokenExpire(Holder.jwt.getExpireTime());
        vo.setRefreshTokenExpire(Holder.jwt.getRefreshTokenExpire());
        stringRedisTemplate.opsForValue().set("api_token|"+user.getUserId()+"|t|"+token,"",Holder.jwt.getExpireTime(), TimeUnit.MILLISECONDS);
        stringRedisTemplate.opsForValue().set("api_token|"+user.getUserId()+"|rt|"+refreshToken,"",Holder.jwt.getRefreshTokenExpire(),TimeUnit.MILLISECONDS);
        return vo;
    }

    @Override
    public TokenVO createSocialToken(ApiOauthDO user) {
        log.info("创建SocialToken:{}",user.getOauthId());
        TokenVO vo = new TokenVO();
        vo.setSocialToken(JWTUtil.sign(user.getOauthId(), user.getOauthId(),Holder.jwt.getSoTokenBizKey(), Holder.jwt.getSocialTokenExpire()));
        vo.setSocialTokenExpire(Holder.jwt.getSocialTokenExpire());
        return vo;
    }

    private boolean notLogout(String token,boolean refresh,String userId) {
        if(refresh){
            return stringRedisTemplate.hasKey("api_token|"+userId+"|rt|"+token);
        }
        return stringRedisTemplate.hasKey("api_token|"+userId+"|t|"+token);
    }

    /**
     * 用户执行更改手机号
     *
     * @param userId
     * @param phone
     * @param smscode
     */
    @Override
    public void bindingPhone(String userId, String phone, String smscode) {
        if(StrUtil.isEmpty(userId)||StrUtil.isEmpty(phone)||StrUtil.isEmpty(smscode)){
            throw new YogoException(EnumErrorCode.paramsFail.getCodeStr());
        }
        Integer nums = apiOauthMapper.selectCount(new QueryWrapper<ApiOauthDO>().lambda().eq(ApiOauthDO::getOauthId, phone));
        if(nums>0){
            throw new YogoException(EnumErrorCode.apiPhoneExist.getCodeStr());
        }
        ApiSmsValidateDO apiSmsValidateDO = apiSmsValidateMapper.selectById(phone);
        if(!(StrUtil.equals(smscode,apiSmsValidateDO.getCode())&&apiSmsValidateDO.getUsed()==0)){
            throw new YogoException(EnumErrorCode.apiAuthorizationRegisterFailed.getCodeStr());
        }
        apiSmsValidateDO.setUsed(1);
        apiSmsValidateMapper.updateById(apiSmsValidateDO);
        ApiOauthDO entity = new ApiOauthDO();
        entity.setOauthId(phone);
        apiOauthMapper.update(entity, new QueryWrapper<ApiOauthDO>().lambda().eq(ApiOauthDO::getUserId,userId).eq(ApiOauthDO::getOauthType,"1"));
    }

    /**
     * 用户去更改手机号
     *
     * @param userId
     * @param smscode
     */
    @Override
    public void changePhoneCheck(String userId, String smscode) {
        if(StrUtil.isEmpty(userId)||StrUtil.isEmpty(smscode)){
            throw new YogoException(EnumErrorCode.paramsFail.getCodeStr());
        }
        ApiOauthDO entity = new ApiOauthDO();
        entity.setUserId(userId);
        entity.setOauthType(1);
        ApiOauthDO apiOauthDO = apiOauthMapper.selectOne(new QueryWrapper<>(entity));
        String phone = apiOauthDO.getOauthId();
        ApiSmsValidateDO apiSmsValidateDO = apiSmsValidateMapper.selectById(phone);
        if(!(StrUtil.equals(smscode,apiSmsValidateDO.getCode())&&apiSmsValidateDO.getUsed()==0)){
            throw new YogoException(EnumErrorCode.apiAuthorizationRegisterFailed.getCodeStr());
        }
        apiSmsValidateDO.setUsed(1);
        apiSmsValidateMapper.updateById(apiSmsValidateDO);
    }
}
